package main

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/studio-b12/gowebdav"
	"gopkg.in/ini.v1"
)

// webdav客户端变量
var client *gowebdav.Client

// 配置文件目录变量
var (
	// 用户home目录
	userHomeDir string
	// 配置基目录 -> 用户目录/.config/wails-note
	configBaseDir string
	// .ini配置文件路径
	iniConfigFilePath string
	// 本地数据存储文件夹路径
	localDataDir string
	// Markdown模板文件路径
	mdTempFilePath string
)

// 坚果云变量
var (
	// Username 用户名
	Username string
	// Password 应用文件夹访问密码
	Password string
	// Foldername 应用文件夹名称
	Foldername string
)

// App struct
type App struct {
	ctx context.Context
}

// RespDate 数据响应结构体
type RespDate struct {
	Code int         `json:"code"` // 响应码
	Data interface{} `json:"data"` // 响应数据
	Msg  string      `json:"msg"`  // 响应消息
}

// NewApp creates a new App application struct
func NewApp() *App {
	return &App{}
}

// 启动时调用的方法
func (a *App) startup(ctx context.Context) {
	// Perform your setup here
	a.ctx = ctx

	// 调用函数进行初始化，创建ini配置文化、local-data目录
	setting()
}

// 初始化设置函数
func setting() {

	// 初始化文件路径
	userHomeDir, _ = os.UserHomeDir()
	configBaseDir = filepath.Join(userHomeDir, ".config", "wails-note")
	localDataDir = filepath.Join(configBaseDir, "local-data")
	iniConfigFilePath = filepath.Join(configBaseDir, "用户配置.ini")
	mdTempFilePath = filepath.Join(localDataDir, "空模板.md")

	// 判断 .config/wails-note 基配置目录是否存在，不存在则创建目录
	if _, err := os.Stat(configBaseDir); os.IsNotExist(err) { // 目录不存在
		// 创建目录
		err = os.MkdirAll(configBaseDir, os.ModePerm)
		if err != nil {
			return
		}
	}

	// 判断local-data目录是否存在，不存在则创建目录
	if _, err := os.Stat(localDataDir); os.IsNotExist(err) { // 目录不存在
		// 创建目录
		err = os.MkdirAll(localDataDir, os.ModePerm)
		if err != nil {
			return
		}
	}

	// 生成ini配置文件  //iniPath := filepath.Join(pwd, "用户配置.ini")
	// ini文件默认内容，坚果云用户名、坚果云存储数据的文件夹名称、访问文件夹的密码
	defaultContent := `[jianguoyun]
username = xxx@qq.com
foldername = xxx
password = xxx`

	// 判断ini配置文件是否存在
	if _, err := os.Stat(iniConfigFilePath); os.IsNotExist(err) { // ini文件不存在
		// 创建ini文件
		err := ioutil.WriteFile(iniConfigFilePath, []byte(defaultContent), os.ModePerm)
		if err != nil {
			return
		}
	}

	// 读取配置文件
	iniFile, err := ini.Load(iniConfigFilePath)
	if err != nil {
		fmt.Println("配置文件读取失败，请检查文件路径", err)
	}
	Username = iniFile.Section("jianguoyun").Key("username").MustString("xxx@qq.com")
	Password = iniFile.Section("jianguoyun").Key("password").MustString("xxx")
	Foldername = iniFile.Section("jianguoyun").Key("foldername").MustString("xxx")

	// 实例化坚果云webdav客户端
	client = gowebdav.NewClient("https://dav.jianguoyun.com/dav/",
		Username, Password)

	//// 创建Markdown模板文件
	//// 不用判断文件是否存在，文件存在直接覆盖
	//bytes := make([]byte, 0)
	//err2 := os.WriteFile(mdTempFilePath, bytes, os.ModePerm)
	//if err2 != nil {
	//	return
	//}
}

// dom准备完毕事件
func (a App) domReady(ctx context.Context) {
	// Add your action here
}

// 退出应用事件
func (a *App) shutdown(ctx context.Context) {
	// Perform your teardown here
}

// @title    GetJGNotebook
// @description   读取坚果云笔记本列表
// @param
// @return		RespDate
func (a *App) GetJGNotebook() RespDate {
	fs, err := client.ReadDir(Foldername)
	if err != nil {
		// fmt.Println(err.Error())
		return RespDate{Code: 500, Msg: err.Error()}
	}

	dirs := make([]string, 0)
	for _, f := range fs {
		if f.IsDir() {
			// fmt.Println(f.Name())
			dirs = append(dirs, f.Name())
		}
	}

	return RespDate{Code: 200, Data: dirs}
}

// @title    GetJGNoteList
// @description   读取坚果云笔记本文件夹下的笔记列表
// @param		dirName		string		笔记本文件夹名称
// @return		RespDate
func (a *App) GetJGNoteList(dirName string) RespDate {
	files, err := client.ReadDir(fmt.Sprintf("%s/%s", Foldername, dirName))
	if err != nil {
		return RespDate{Code: 500, Msg: err.Error()}
	}

	fs := make([]string, 0)
	for _, file := range files {
		// fmt.Println("坚果云文件夹下笔记：" + file.Name())
		fs = append(fs, file.Name())
	}

	return RespDate{Code: 200, Data: fs}
}

// @title    CreateNotebook
// @description   创建笔记本文件夹
// @param		dirName		string		笔记本文件夹名称
// @return		RespDate
func (a *App) CreateNotebook(dirName string) RespDate {
	// 创建本地笔记本文件夹
	folderPath := filepath.Join(localDataDir, dirName)
	// fmt.Println(folderPath)
	if _, err := os.Stat(folderPath); os.IsNotExist(err) { // 文件夹不存在
		// 创建文件夹
		err := os.MkdirAll(folderPath, os.ModePerm)
		if err != nil {
			return RespDate{Code: 500, Msg: err.Error()}
		}

		// 授权
		os.Chmod(folderPath, os.ModePerm)

		// 创建云端笔记本文件夹
		//定义一个同步等待的组
		var wg sync.WaitGroup
		var inErr error
		go func() {
			wg.Add(1)
			path := fmt.Sprintf("%s/%s", Foldername, dirName)
			// 判断端笔记本文件夹是否存在
			if _, err := client.Stat(path); err != nil { // 文件夹不存在
				err := client.MkdirAll(path, 0644)
				if err != nil {
					// fmt.Println("坚果云文件夹创建失败")
					inErr = err
				}
			}

			defer wg.Done()
		}()

		wg.Wait()

		if inErr != nil {
			return RespDate{Code: 500, Msg: "坚果云文件夹创建失败"}
		}

		return RespDate{Code: 200}
	}

	return RespDate{Code: 400, Msg: "文件夹已存在"}
}

// @title    GetDirs
// @description   遍历目录
// @param
// @return		RespDate
func (a *App) GetDirs() RespDate {
	dirs := make([]string, 0)

	fs, err := ioutil.ReadDir(localDataDir)
	if err != nil {
		return RespDate{Code: 500, Msg: err.Error()}
	}

	for _, f := range fs {
		if f.IsDir() {
			// fmt.Println(f.Name())
			dirs = append(dirs, f.Name())
		}
	}

	return RespDate{Code: 200, Data: dirs}
}

// @title    CreateNoteFile
// @description   创建临时笔记Markdown文件
// @param		dirName		string		笔记本文件夹名称
// @return		RespDate
func (a *App) CreateNoteFile(dirName string) RespDate {
	// 临时笔记文件名
	newFileName := time.Now().Local().Format("20060102150405")
	// 临时笔记文件路径
	destFilePath := filepath.Join(localDataDir, dirName, (newFileName + ".md"))
	// 创建临时笔记文件
	err := ioutil.WriteFile(destFilePath, make([]byte, 0), os.ModePerm)
	if err != nil {
		return RespDate{Code: 400, Msg: "文件创建失败"}
	}

	return RespDate{Code: 200, Data: newFileName, Msg: "文件创建成功"}

	//// 复制模板文件内容到临时笔记文件
	//// 判断md模板文件是否存在
	//_, err := os.Stat(mdTempFilePath)
	//if err == nil { // 文件存在
	//	// 读取md模板文件
	//	input, err := ioutil.ReadFile(mdTempFilePath)
	//	if err != nil {
	//		// fmt.Println(err)
	//		return RespDate{Code: 500, Msg: "文件读取失败"}
	//	}
	//
	//	// 复制md模板文件内容
	//	err = ioutil.WriteFile(destFilePath, input, os.ModePerm)
	//	if err != nil {
	//		// fmt.Println("创建失败：", destinationFile)
	//		// fmt.Println(err)
	//		return RespDate{Code: 500, Msg: "文件创建失败"}
	//	}
	//
	//	// 授权
	//	os.Chmod(destFilePath, os.ModePerm)
	//	return RespDate{Code: 200, Data: newFileName, Msg: "文件创建成功"}
	//}

	//return RespDate{Code: 400, Msg: "Markdown模板文件不存在"}
}

// @title    SaveNote
// @description   保存笔记(文章)
// @param		dirName		string		笔记本文件夹名称
// @param		noteName		string		临时笔记名
// @param		noteTitle		string		笔记标题
// @param		noteContent		string		笔记（文章）内容
// @return		RespDate
func (a *App) SaveNote(dirName, noteName, noteTitle, noteContent string) RespDate {
	// md文件路径
	oldFilePath := filepath.Join(localDataDir, dirName, (noteName + ".md"))
	// 重命名文件路径
	newFilePath := filepath.Join(localDataDir, dirName, (noteTitle + ".md"))

	// 写入内容到文件
	err := ioutil.WriteFile(oldFilePath, []byte(noteContent), os.ModePerm)
	if err != nil {
		//fmt.Println(err.Error())
		return RespDate{Code: 500, Msg: err.Error()}
	}

	// 文件重命名
	err = os.Rename(oldFilePath, newFilePath)
	if err != nil {
		//fmt.Println(err.Error())
		return RespDate{Code: 500, Msg: err.Error()}
	}

	// 上传到坚果云
	webdavFilePath := fmt.Sprintf("%s/%s/%s.md", Foldername, dirName, noteTitle)
	bytes, _ := ioutil.ReadFile(newFilePath)
	err = client.Write(webdavFilePath, bytes, 0644)
	if err != nil {
		return RespDate{Code: 500, Msg: err.Error()}
	}

	return RespDate{Code: 200, Data: newFilePath}
}

// @title    ReadNoteFile
// @description   读取笔记文件内容
// @param		dirName		string		笔记本文件夹名称
// @param		fileName		string		笔记文件名
// @return		RespDate
func (a *App) ReadNoteFile(dirName, fileName string) RespDate {
	filePath := filepath.Join(localDataDir, dirName, (fileName + ".md"))
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		return RespDate{Code: 500, Msg: err.Error()}
	}
	return RespDate{Code: 200, Data: string(data)}
}

// @title    RemoveNote
// @description   删除笔记
// @param		dirName		string		笔记本文件夹名称
// @param		fileName		string		笔记文件名
// @return		RespDate
func (a *App) RemoveNote(dirName, fileName string) RespDate {
	var err1 error
	var err2 error
	var wg sync.WaitGroup

	localFilePath := filepath.Join(localDataDir, dirName, (fileName + ".md"))
	// 删除本地端文件
	go func() {
		wg.Add(1)
		// 删除本地端文件
		err1 = os.Remove(localFilePath)
		defer wg.Done()
	}()

	// 删除云端文件
	go func() {
		wg.Add(1)
		webdavFilePath := fmt.Sprintf("%s/%s/%s.md", Foldername, dirName, fileName)
		_, err3 := client.Stat(webdavFilePath)
		if err3 == nil { // 文件存在
			// 删除云端文件
			err2 = client.Remove(webdavFilePath)
		}

		defer wg.Done()
	}()

	wg.Wait()

	if err1 != nil {
		return RespDate{Code: 500, Msg: err1.Error()}
	}
	if err2 != nil {
		return RespDate{Code: 500, Msg: err2.Error()}
	}

	return RespDate{Code: 200}
}

// @title    RemoveTempNote
// @description   删除临时笔记
// @param		dirName		string		笔记本文件夹名称
// @param		fileName		string		笔记文件名
// @return		RespDate
func (a *App) RemoveTempNote(dirName, fileName string) RespDate {
	localFilePath := filepath.Join(localDataDir, dirName, (fileName + ".md"))
	err := os.Remove(localFilePath)
	if err != nil {
		return RespDate{Code: 500, Msg: err.Error()}
	}
	return RespDate{Code: 200}
}

// @title    RemoveNotebook
// @description   删除笔记本
// @param		dirName		string		笔记本文件夹名称
// @return		RespDate
func (a *App) RemoveNotebook(dirName string) RespDate {
	var err1 error
	var err2 error
	var wg sync.WaitGroup

	// 删除本地端文件夹
	localDirPath := filepath.Join(localDataDir, dirName)
	go func() {
		wg.Add(1)
		// 删除本地端文件夹
		err1 = os.RemoveAll(localDirPath)
		defer wg.Done()
	}()

	// 删除云端文件
	go func() {
		wg.Add(1)
		webdavDirPath := fmt.Sprintf("%s/%s", Foldername, dirName)
		// 删除云端文件
		err2 = client.RemoveAll(webdavDirPath)
		defer wg.Done()
	}()

	wg.Wait()

	if err1 != nil {
		return RespDate{Code: 500, Msg: err1.Error()}
	}
	if err2 != nil {
		return RespDate{Code: 500, Msg: err2.Error()}
	}

	return RespDate{Code: 200}
}

// @title    SaveSettings
// @description   保存设置
// @return		RespDate
func (a *App) SaveSettings(jgUserName string, jgAppName string, jgAppPwd string) RespDate {
	defaultContent := `[jianguoyun]
username = %s
foldername = %s
password = %s`

	txt := fmt.Sprintf(defaultContent, jgUserName, jgAppName, jgAppPwd)

	// 覆盖ini文件
	err := ioutil.WriteFile(iniConfigFilePath, []byte(txt), os.ModePerm)
	if err != nil {
		return RespDate{Code: 500, Msg: err.Error()}
	}

	return RespDate{Code: 200}
}

// @title    SyncToCloud
// @description   同步到云端（坚果云）
// @return		RespDate
func (a *App) SyncToCloud() RespDate {
	// 文件夹名称数组
	folders := make([]string, 0)

	// 遍历文件夹名称
	fs, err := ioutil.ReadDir(localDataDir)
	if err != nil {
		return RespDate{Code: 500, Msg: err.Error()}
	}
	// 遍历本地笔记本文件夹
	for _, f := range fs {
		if f.IsDir() {
			folders = append(folders, f.Name())
		}
	}

	// 遍历文件夹名称并同步到云端
	for _, folderName := range folders {
		localFolderPath := filepath.Join(localDataDir, folderName)
		webdavFolderPath := fmt.Sprintf("%s/%s", Foldername, folderName)
		// 判断云端笔记本文件夹是否存在
		_, err2 := client.Stat(webdavFolderPath)
		if err2 != nil { // 云端文件夹不存在
			// 创建文件夹
			err3 := client.Mkdir(webdavFolderPath, 0644)
			if err3 != nil {
				return RespDate{Code: 500, Msg: err3.Error()}
			}
		}

		// 遍历 笔记/文章 md文件并上传到云
		files, _ := ioutil.ReadDir(localFolderPath)
		for _, file := range files {
			fileName := file.Name()
			// fmt.Println("同步到云时遍历的本地文件：" + fileName)
			// 读取文件
			bytes, _ := ioutil.ReadFile(filepath.Join(localFolderPath, fileName))
			// 上传到云端(不存在则创建，存在则覆盖)
			err4 := client.Write(fmt.Sprintf("%s/%s", webdavFolderPath, file.Name()), bytes, 0644)
			if err4 != nil {
				return RespDate{Code: 500, Msg: err4.Error()}
			}
		}
	}
	return RespDate{Code: 200}
}

// @title    DownToLocal
// @description   同步到本地
// @return		RespDate
func (a *App) DownToLocal() RespDate {
	folders := make([]string, 0)

	fs, err := client.ReadDir(Foldername)
	if err != nil {
		return RespDate{Code: 500, Msg: err.Error()}
	}
	// 获取云端文件夹
	for _, f := range fs {
		if f.IsDir() {
			folders = append(folders, f.Name())
		}
	}

	for _, folderName := range folders {
		localFolderPath := filepath.Join(localDataDir, folderName)
		// fmt.Println("文件夹:" + localFolderPath)
		// 云端文件夹路径
		webdavFolderPath := fmt.Sprintf("%s/%s", Foldername, folderName)
		// fmt.Println("云端文件夹:" + webdavFolderPath)
		// 判断本地笔记本文件夹是否存在
		_, err2 := os.Stat(localFolderPath)
		if os.IsNotExist(err2) { // 本地文件夹不存在
			// 创建本地文件夹
			err3 := os.Mkdir(localFolderPath, os.ModePerm)
			if err3 != nil {
				return RespDate{Code: 500, Msg: err3.Error()}
			}
			// fmt.Println("创建本地文件夹成功")
		}

		// 遍历 笔记/文章 md文件并下载到本地
		files, _ := client.ReadDir(webdavFolderPath)
		for _, file := range files {
			fileName := file.Name()
			// fmt.Println("同步到本地时遍历的云端文件：" + fileName)
			// 读取云端文件
			bytes, _ := client.Read(fmt.Sprintf("%s/%s", webdavFolderPath, fileName))
			// 下载文件到本地（不存在则创建，存在则覆盖）
			fPath := filepath.Join(localFolderPath, fileName)
			// fmt.Println("同步保存到得本地文件路径：" + fPath)
			ioutil.WriteFile(fPath, bytes, os.ModePerm)
			// 授权
			os.Chmod(fPath, os.ModePerm)
		}
	}

	return RespDate{Code: 200}
}
